﻿using C1.Util.DX.Direct2D.Effects;
using C1.WPF.Chart;
using C1.WPF.Chart.Annotation;
using Model;
using Model.Modules;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Lifetime;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Annotations;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;
using static log4net.Appender.ColoredConsoleAppender;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using Utility;
using Proxy;

namespace SteelDetection.UIControl
{
    /// <summary>
    /// UCRadoChart.xaml 的交互逻辑
    /// </summary>
    public partial class UCRadoChart : UserControl
    {
        #region 变量
        List<UCRulerchart> charts = new List<UCRulerchart>();
        List<Axis> axisxs = new List<Axis>(), axisys = new List<Axis>();
        public double AxisXMax { get { return axisXMax; } set { axisXMax = value; axisxs.All(a => { a.Max = AxisXMax; return true; }); } }
        private double axisXMax;
        public double AxisXMin { get { return axisXMin; } set { axisXMin = value; axisxs.All(a => { a.Min = AxisXMin; return true; }); } }
        private double axisXMin;
        public double AxisYMax { get { return axisYMax; } set { axisYMax = value; axisys.All(a => { a.Max = AxisYMax; return true; }); } }
        private double axisYMax;
        public double AxisYMin { get { return axisYMin; } set { axisYMin = value; axisys.All(a => { a.Min = AxisYMin; return true; }); } }
        private double axisYMin;
        public Shape ActiveAnnotiaton { get { return activeAnnotiaton; } set { activeAnnotiaton = value; } }
        private Shape activeAnnotiaton;
        public UCRulerchart ActiveRulerchart { get { return activeRulerchart; } set { activeRulerchart = value; } }
        private UCRulerchart activeRulerchart;
        bool IsAllChartLoaded = false;
        #endregion

        /// <summary>
        /// 当所有chart加载完成后触发
        /// </summary>
        public event Action<List<UCRulerchart>> AllChartLoaded;

        /// <summary>
        /// 当前钢材缺陷变化时触发
        /// </summary>
        public event Action<steel_defect> CurrentSteelDefectChanged;

        public event Action<IList<steel_defect>> SelectedSteelDefectsChanged;


        ObservableCollection<ChartInfo> chartinofs = new ObservableCollection<ChartInfo>();
        int loadedChart = 0;
        public UCRadoChart()
        {
            InitializeComponent();
            SetChartSource();
            AllChartLoaded += (charts) =>
            {
                IsAllChartLoaded = true;
                charts.All(chart =>
                {
                    chart.MouseLeftButtonDown += flexChart_MouseLeftButtonDown;
                    chart.MouseLeftButtonUp += flexChart_MouseLeftButtonUp;
                    chart.MouseRightButtonDown += flexChart_MouseRightButtonDown;
                    chart.MouseRightButtonUp += flexChart_MouseRightButtonUp;
                    chart.MouseMove += flexChart_MouseMove;
                    //chart.MouseLeftButtonUp += rado_MouseLeftButtonDown; 
                    return true;
                });
            };
            SelectedSteelDefectsChanged += (steeldefects) =>
            {
            };
            Unloaded += UCRadoChart_Unloaded;
        }

        private void UCRadoChart_Unloaded(object sender, RoutedEventArgs e)
        {
            charts.Clear();
            IsAllChartLoaded = false;
            loadedChart = 0;
        }

        private void UCRulerchart_Loaded(object sender, RoutedEventArgs e)
        {
            if (loadedChart == chartinofs.Count)
                return;
            var chart = (sender as UCRulerchart);
            charts.Add(chart);
            axisys.Add(chart.rulerchart.AxisY);
            axisxs.Add(chart.rulerchart.AxisX);

            loadedChart++;
            if (loadedChart == chartinofs.Count)
            {
                AllChartLoaded.Invoke(charts);
            }
        }


        private void Yscroller_ValueChanged(object sender, EventArgs e)
        {
            if (Yscroller.LowerValue == Yscroller.UpperValue)
                return;
            AxisYMax = Yscroller.UpperValue;
            AxisYMin = Yscroller.LowerValue;
        }
        private void Xscroller_ValueChanged(object sender, EventArgs e)
        {
            if (Xscroller.LowerValue == Xscroller.UpperValue)
                return;
            AxisXMax = Xscroller.UpperValue;
            AxisXMin = Xscroller.LowerValue;
        }

        public async void UpdateRectAnnos(ObservableCollection<steel_defect> defectinfos)
        {
            if (!IsAllChartLoaded)
                await Task.Factory.StartNew(() =>
                {
                    while (!IsAllChartLoaded)
                    {
                        Thread.Sleep(100);
                    }
                });
            if (!defectinfos.Any())
                return;
            var maxY = defectinfos.Max(m => m.real_y + m.real_h);
            if (Yscroller.Maximum < maxY + 500)
                Yscroller.Maximum = (double)maxY * 1.1;
            Yscroller.UpperValue = Yscroller.Maximum;
            var maxX = defectinfos.Max(m => m.real_x + m.real_w);
            if (Xscroller.Maximum < maxX + 1)
                Xscroller.Maximum = (double)maxX + 1;
            Xscroller.UpperValue = Xscroller.Maximum;

            var groups = defectinfos.GroupBy(m => m.surface_id);

            foreach (var group in groups)
            {
                int chartindex = group.Key - 1;//surface_id定义为从1开始，因此-1.
                _ = Task.Run<List<DefectAnnoInfo>>(() =>
                {
                    List<DefectAnnoInfo> defectAnnos = new List<DefectAnnoInfo>();
                    ;
                    foreach (var defectinfo in group)
                    {
                        defectAnnos.Add(new DefectAnnoInfo()
                        {
                            rect = new Rect((double)defectinfo.real_x, (double)defectinfo.real_y, (double)defectinfo.real_w, (double)defectinfo.real_h),
                            type = (int)defectinfo.type,
                            steel_defect = defectinfo
                        });
                    }

                    _ = charts[chartindex].Dispatcher.BeginInvoke(new Action(() =>
                    {
                        charts[chartindex].AddDefects(defectAnnos);
                    }));
                    return defectAnnos;
                });
                //charts[chartindex].rulerchart.Invalidate();

            }
        }

        public void ClearAnnos()
        {
            Xscroller.LowerValue = 0;
            Yscroller.LowerValue = 0;
            Xscroller.Maximum = 10;
            Yscroller.Maximum = 100;
            Xscroller.UpperValue = 10;
            Yscroller.UpperValue = 100;
            foreach (var item in charts)
            {
                var rulerchart = item as UCRulerchart;
                rulerchart.Dispatcher.BeginInvoke(new Action(() =>
                {
                    rulerchart.ClearAnno();
                }));
            }
        }

        public void MoveCurrentLeftEdge(int currentLeftEdge)
        {
            foreach (var item in charts)
            {
                var rulerchart = item as UCRulerchart;
                rulerchart.Dispatcher.BeginInvoke(new Action(() =>
                {
                    rulerchart.UpdateScanLine(currentLeftEdge);
                }), System.Windows.Threading.DispatcherPriority.Input);
            }
            Yscroller.Maximum = Yscroller.Maximum + 1;
            Yscroller.UpperValue = Yscroller.Maximum + 1;
            if (Yscroller.Maximum < currentLeftEdge)
            {
                Yscroller.Maximum = Math.Max(1000, currentLeftEdge * 1.1);
                Yscroller.UpperValue = Yscroller.Maximum;
            }

        }

        public void SetChartSource()
        {
            var surfacetypes = SurfaceTypes.GetInstance();
            var chartinfos = new ObservableCollection<ChartInfo>();
            foreach (var item in surfacetypes)
            {
                chartinfos.Add(new ChartInfo() { ChartName = item.name, Tag = item.id.ToString() });
            }

            this.chartinofs = chartinfos;
            chartpanel.ItemsSource = chartinfos;
        }





        C1.WPF.Chart.Annotation.Rectangle CurrentRectangle = null;
        string CurrentChartTag = "";
        private void rado_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var rado = sender as UCRulerchart;

            //设置了鼠标的误差量，计算得到纵横数据和像素的转换比例，用于后面的判断，给定一个误差量，如果点击的点在误差量内（缺陷范围上下左右都加上eroorAmount像素 * xy对应比例），则认为点击的是缺陷
            var zeropoint = rado.rulerchart.DataToPoint(new Point(0, 0));
            var onepoint = rado.rulerchart.DataToPoint(new Point(1, 1));
            var xscale = 1 / (onepoint.X - zeropoint.X);
            var yscale = 1 / (onepoint.Y - zeropoint.Y);
            double eroorAmount = 3;//像素

            var input = e.GetPosition(rado.rulerchart);
            var datapoint = rado.rulerchart.PointToData(input);
            //var hitinfo = rado.rulerchart.HitTest(e.GetPosition(rado.rulerchart));

            bool hasdefect = false;
            steel_defect steeldefect = null;
            Shape selectedanno = null;
            foreach (var anno in rado.annolayer.Annotations)
            {
                if (anno is C1.WPF.Chart.Annotation.Rectangle)
                {
                    var rectangle = anno as C1.WPF.Chart.Annotation.Rectangle;
                    var steel_defect = (rectangle.GetValue(TagProperty) as DefectAnnoInfo).steel_defect as steel_defect;
                    //此处使用了前面的误差量eroorAmount，给缺陷范围上下左右都加上eroorAmount像素 * xy对应比例，作为判断范围
                    Rect rect = new Rect((double)steel_defect.real_x - eroorAmount * xscale, (double)steel_defect.real_y - eroorAmount * yscale, (double)steel_defect.real_w + eroorAmount * xscale * 2, (double)steel_defect.real_h + eroorAmount * yscale * 2);
                    if (rect.Contains(datapoint))
                    {
                        steeldefect = steel_defect;
                        hasdefect = true;
                        selectedanno = anno as Shape;
                        break;
                    }
                }
                else if (anno is Circle)
                {
                    var circle = anno as Circle;
                    var point = rado.rulerchart.DataToPoint(circle.Location);

                    var distance = Math.Sqrt((Math.Pow(input.X - point.X, 2) + Math.Pow(input.Y - point.Y, 2)));
                    if (distance <= circle.Radius)
                    {
                        steeldefect = (circle.GetValue(TagProperty) as DefectAnnoInfo).steel_defect as steel_defect;
                        hasdefect = true;
                        selectedanno = anno as Shape;
                        break;
                    }
                }
                else if (anno is Square)
                {
                    var square = anno as Square;
                    var point = rado.rulerchart.DataToPoint(square.Location);
                    if (Math.Abs(point.X - input.X) <= square.Length / 2 && Math.Abs(point.Y - input.Y) <= square.Length / 2)
                    {
                        steeldefect = (square.GetValue(TagProperty) as DefectAnnoInfo).steel_defect as steel_defect;
                        hasdefect = true;
                        selectedanno = anno as Shape;
                        break;
                    }
                }
            }
            //在上方就可以设置新的缺陷图标高亮，下方只需要清楚原来高亮接，当然在下方统一设置会更加方便
            if (hasdefect)
            {
                CurrentSteelDefectChanged.Invoke(steeldefect);
            }
            else
            {
                CurrentSteelDefectChanged.Invoke(null);
            }
        }
        private void SetHighLight(Shape selectedanno, UCRulerchart rado)
        {
            //清除高亮
            var rectangle = ActiveAnnotiaton as C1.WPF.Chart.Annotation.Rectangle;
            if (rectangle != null)
            {
                rectangle.Style.StrokeThickness = 2;
            }
            else
            {
                var iconshape = ActiveAnnotiaton as C1.WPF.Chart.Annotation.Shape;
                rado.setNormalStyle(iconshape);
            }
            if (ActiveRulerchart != null)
                ActiveRulerchart.rulerchart.Invalidate();

            ActiveRulerchart = rado;
            ActiveAnnotiaton = selectedanno;

            //设置高亮
            rectangle = ActiveAnnotiaton as C1.WPF.Chart.Annotation.Rectangle;
            if (rectangle != null)
            {
                rectangle.Style.StrokeThickness = 8;
            }
            else
            {
                var iconshape = ActiveAnnotiaton as C1.WPF.Chart.Annotation.Shape;
                rado.setHighLightStyle(iconshape);
            }
            ActiveRulerchart.rulerchart.Invalidate();
        }


        private SolidColorBrush getColorByType(int typeid)
        {
            var colorstr = "Green";
            var list = StaticData.DefectTypes.Where(t => t.id == typeid);
            if (list.Count() == 1)
                colorstr = list.First().color;
            return new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(colorstr));
        }

        bool rightbtnpressing = false;
        bool leftbtnpressing = false;
        Point ptStart = new Point();
        private void flexChart_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!leftbtnpressing)
                return;
            //结束绘制
            leftbtnpressing = false;
            var rulerchart = (sender as UCRulerchart);
            rulerchart.leftDragFrameContainer.Visibility = Visibility.Collapsed;

            rulerchart.leftDragFrame.Rect = new Rect();
            var currentPosition = e.GetPosition(rulerchart.rulerchart);

            if (ptStart.X != currentPosition.X && ptStart.Y != currentPosition.Y)
            {
                //左键框框选响应方法,放大到指定区域
                ZoomToRect(sender, ptStart, currentPosition);
            }
            else if (ptStart.X == currentPosition.X && ptStart.Y == currentPosition.Y)
            {
                //左键单击响应方法
                rado_MouseLeftButtonDown(sender, e);
            }
            ptStart = new Point();
            rulerchart.rulerchart.ReleaseMouseCapture();


            //Clean up

        }

        private void flexChart_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var rulerchart = (sender as UCRulerchart);

            ptStart = e.GetPosition(rulerchart.rulerchart);
            if (ptStart.X < 22)
                return;
            var point = rulerchart.rulerchart.PointToData(ptStart);
            var plotrect = rulerchart.rulerchart.PlotRect;

            //判断如果点击的是非图表内容区域，则结束
            if (ptStart.X < plotrect.Left - 20 || ptStart.Y > plotrect.Bottom + 16)
                return;
            rulerchart.leftDragFrameContainer.Visibility = Visibility.Visible;
            //    // Start zooming
            leftbtnpressing = true;
            //    // Save starting point of selection rectangle

            rulerchart.rulerchart.CaptureMouse();
        }
        //绘制左键按下时的区域
        private void DrawLeftFrameRectangle(object sender, Point p1, Point p2)
        {
            // Normalize the rectangle
            var rc = new Rect(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y),
                Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y));

            // Draw the reversible frame
            (sender as UCRulerchart).leftDragFrame.Rect = rc;
        }

        private void ZoomToRect(object sender, Point ptStart, Point ptLast)
        {
            var rulerchart = (sender as UCRulerchart);
            var p1 = rulerchart.rulerchart.PointToData(ptStart);
            var p2 = rulerchart.rulerchart.PointToData(ptLast);

            Yscroller.LowerValue = Math.Min(p1.Y, p2.Y);
            Yscroller.UpperValue = Math.Max(p1.Y, p2.Y);
            Xscroller.LowerValue = Math.Min(p1.X, p2.X);
            Xscroller.UpperValue = Math.Max(p1.X, p2.X);
        }




        private void flexChart_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            var rulerchart = (sender as UCRulerchart);
            rulerchart.rightDragFrameContainer.Visibility = Visibility.Visible;
            //    // Start zooming
            rightbtnpressing = true;
            //    // Save starting point of selection rectangle
            ptStart = e.GetPosition(rulerchart.rulerchart);
            rulerchart.rulerchart.CaptureMouse();
        }
        private void flexChart_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            var rulerchart = (sender as UCRulerchart);
            //End zooming
            rightbtnpressing = false;
            rulerchart.rightDragFrameContainer.Visibility = Visibility.Collapsed;
            rulerchart.rightDragFrame.Rect = new Rect();
            var currentPosition = e.GetPosition(rulerchart.rulerchart);
            //这里选中一个区域中的缺陷展示在列表
            //获取在选中范围内的标注
            List<steel_defect> selected_steel_defects = new List<steel_defect>();
            var p1 = rulerchart.rulerchart.PointToData(ptStart);
            var p2 = rulerchart.rulerchart.PointToData(currentPosition);
            double minx = Math.Min(p1.X, p2.X);
            double maxx = Math.Max(p1.X, p2.X);
            double miny = Math.Min(p1.Y, p2.Y);
            double maxy = Math.Max(p1.Y, p2.Y);
            Rect rect = new Rect(ptStart, currentPosition);
            foreach (var anno in rulerchart.annolayer.Annotations)
            {
                if (!(anno is Shape))
                    continue;
                var annopoint = rulerchart.rulerchart.DataToPoint((anno as Shape).Location);
                if (rect.Contains(annopoint))
                {
                    steel_defect defect = (((C1.WPF.Chart.Annotation.Shape)anno).GetValue(TagProperty) as DefectAnnoInfo).steel_defect as steel_defect;
                    selected_steel_defects.Add(defect);
                }

                //if (defect.real_x>= minx && defect.real_x <= maxx&& defect.real_y >= miny && defect.real_y <= maxy)
                //{
                //    selected_steel_defects.Add(defect);
                //}
            }
            if (selected_steel_defects.Count > 0)
            {
                SelectedSteelDefectsChanged.Invoke(selected_steel_defects);
                CurrentSteelDefectChanged.Invoke(selected_steel_defects.First());
            }


            //Clean up
            ptStart = new Point();
            rulerchart.rulerchart.ReleaseMouseCapture();
        }
        //绘制右键按下时的区域
        private void DrawRightFrameRectangle(object sender, Point p1, Point p2)
        {
            // Normalize the rectangle
            var rc = new Rect(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y),
                Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y));

            // Draw the reversible frame
            (sender as UCRulerchart).rightDragFrame.Rect = rc;
        }


        private void ZoomInButton_Click(object sender, RoutedEventArgs e)
        {
            var x_distance = Xscroller.UpperValue - Xscroller.LowerValue;
            Xscroller.LowerValue = Xscroller.LowerValue + x_distance * (1 - 0.95);
            Xscroller.UpperValue = Xscroller.UpperValue - x_distance * (1 - 0.95);
            var y_distance = Yscroller.UpperValue - Yscroller.LowerValue;
            Yscroller.LowerValue = Yscroller.LowerValue + y_distance * (1 - 0.95);
            Yscroller.UpperValue = Yscroller.UpperValue - y_distance * (1 - 0.95);
        }
        private void ZoomOutButton_Click(object sender, RoutedEventArgs e)
        {
            var x_distance = Xscroller.UpperValue - Xscroller.LowerValue;
            Xscroller.LowerValue = Math.Max(Xscroller.Minimum, Xscroller.LowerValue - x_distance * (1.0 / 0.95 - 1));
            Xscroller.UpperValue = Math.Min(Xscroller.Maximum, Xscroller.UpperValue + x_distance * (1.0 / 0.95 - 1));
            var y_distance = Yscroller.UpperValue - Yscroller.LowerValue;
            Yscroller.LowerValue = Math.Max(Yscroller.Minimum, Yscroller.LowerValue - y_distance * (1.0 / 0.95 - 1));
            Yscroller.UpperValue = Math.Min(Yscroller.Maximum, Yscroller.UpperValue + y_distance * (1.0 / 0.95 - 1));
        }

        private void RetoreButton_Click(object sender, RoutedEventArgs e)
        {
            Xscroller.LowerValue = Xscroller.Minimum;
            Xscroller.UpperValue = Xscroller.Maximum;
            Yscroller.LowerValue = Yscroller.Minimum;
            Yscroller.UpperValue = Yscroller.Maximum;
        }



        private void flexChart_MouseMove(object sender, MouseEventArgs e)
        {
            var rulerchart = (sender as UCRulerchart);
            // when zooming update selection range
            if (rightbtnpressing)
            {
                var currentPosition = e.GetPosition(rulerchart.rulerchart);
                Point ptCurrent = currentPosition;
                // Draw new frame
                DrawRightFrameRectangle(sender, ptStart, ptCurrent);
            }
            if (leftbtnpressing)
            {
                var currentPosition = e.GetPosition(rulerchart.rulerchart);
                Point ptCurrent = currentPosition;
                // Draw new frame
                DrawLeftFrameRectangle(sender, ptStart, ptCurrent);
            }
        }

        public void ChangeCurrentSteelDefectAnno(steel_defect steeldefect)
        {
            foreach (var rado in charts)
            {
                foreach (var anno in rado.annolayer.Annotations)
                {
                    Shape shapeanno = anno as Shape;
                    if (shapeanno == null)
                        continue;
                    if ((shapeanno.GetValue(TagProperty) as DefectAnnoInfo).steel_defect.id == steeldefect.id)
                    {
                        SetHighLight(shapeanno, rado);
                    }
                }
            }
        }




    }
    public class ChartInfo : INotifyPropertyChanged
    {
        public string ChartName { get; set; }
        public string Tag { get; set; }


        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class DefectAnnoInfo
    {
        public Rect rect { get; set; }
        public int type { get; set; }
        public steel_defect steel_defect { get; set; }
    }
}
